package com.wlz.security.session.config;

import com.wlz.security.session.strategy.MyExpiredSessionStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 *  1. 获取 用户 信息 (访问  /admin/index)
 *  2. 会话控制
 *      1）always ：      如果session不存在总是需要创建
 *      2）ifRequired：   如果需要就创建一个session（默认）登录时
 *      3）never：        Spring Security 将不会创建session，但是如果应用中其他地方创建了session，那么Spring Security将会使用它
 *      4）stateless：    Spring Security将绝对不会创建session，也不使用session。并且它会暗示不使用
 *             cookie，所以每个请求都需要重新进行身份验证。这种无状态架构适用于REST API
 *              及其无状态认证机制。
 *  3. 会话超时
 *      1）可以在sevlet容器中设置Session的超时时间，如下设置Session有效期为600s； spring boot 配置文件：
 *          server.servlet.session.timeout=60s
 *       2）注意：session最低60s。
 *  4. 会话并发控制
 *      用户在这个手机登录后，他又在另一个手机登录相同账户，对于之前登录的账户 是否需要被挤兑，
 *      或者说 在第二次登录时限制它登录 ，更或者像腾讯视频VIP账号一样，最多只能五个人同时登录，第六个人限制登录。
 *      1)
 *          maximumSessions：最大会话数量，设置为1表示一个用户只能有一个会话
 *          expiredSessionStrategy：会话过期策略
 *      2) 阻止用户第二次登录
 *          maxSessionsPreventsLogin：boolean值, 当达到 maximumSessions设置的最大会话个数时阻止登录。
 *  5. 集群session
 *      1) 引入spring session依赖
 *      2) 修改application.yaml
 *      3) 测试
 *          启动两个服务8080，8081 ，其中一个登录后访问http://localhost:8080/admin/index，另外一个不需
 *          要登录就可以访问
 *  6. 安全会话cookie (我们可以使用httpOnly和secure标签来保护我们的会话cookie：)
 *     1)  httpOnly：如果为true，那么浏览器脚本将无法访问cookie (server.servlet.session.cookie.http-only=true)
 *     2)  secure：如果为true，则cookie将仅通过HTTPS连接发送   (server.servlet.session.cookie.secure=true)
 *
 * @author wlz
 */
@Configuration
public class WebSecurtiyConfig4 extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService myUserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
    }


    /**
     *   测试
     *  1. 使用chrome浏览器，先登录，再访问http://localhost:8080/admin/index
     *  2. 使用firefox浏览器，再登录，再访问http://localhost:8080/admin/index
     *  3. 使用chrome浏览器，重新访问http://localhost:8080/admin/index，会执行
     *      expiredSessionStrategy，页面上显示”您已被挤兑下线！“
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin();
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .invalidSessionUrl("/session/invalid")  // 配置 session超时之后，跳转的路径
                .maximumSessions(1)  // 最大会话数量，设置为1表示一个用户只能有一个会话
                .expiredSessionStrategy(new MyExpiredSessionStrategy()) // 会话过期策略
                .maxSessionsPreventsLogin(true); // 当达到 maximumSessions设置的最大会话个数时阻止登录。

        http.authorizeRequests()
                .antMatchers("/login","/session/invalid").permitAll()
                .anyRequest().authenticated();
        http.csrf().disable();
    }


    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}
